﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Zen4SyncOrchestratorServices;
using Zen4SyncModel;
using Zen4SyncPOCO.POCO.Admin;
using System.ServiceModel;
using Zen4SyncTestServerServices;
using System.Configuration;
using System.Data.Objects;

namespace Zen4SyncOrchestrator
{
    public class OrchestratorServices : IOrchestratorServices
    {        
        private DebugLogger.DebugLogger logger;

        private bool debugLogEnabled;

        private static ModelConverter _z4sModelConverter = null;

        internal static ModelConverter Z4SModelConverter
        {
            get
            {
                if ( _z4sModelConverter == null ) _z4sModelConverter = new ModelConverter();

                return _z4sModelConverter;
            }
        }

        public void StartTestSession( Guid testSessionId, string zen4SyncSymmetricKeyPassword )
        {
            string cfgEnableDebugLog = ConfigurationManager.AppSettings["OrchestratorEnableDebugLog"];
            string cfgLogFilePath = ConfigurationManager.AppSettings["OrchestratorDebugLogFilePath"];

            if (!String.IsNullOrEmpty(cfgEnableDebugLog) && !String.IsNullOrEmpty(cfgLogFilePath))
            {
                debugLogEnabled = Boolean.Parse(cfgEnableDebugLog);
            }
            else
            {
                debugLogEnabled = false;
            }

            if (debugLogEnabled)
            {
                logger = new DebugLogger.DebugLogger(cfgLogFilePath);
                logger.Open();
            }


            Dictionary<Guid, List<ClientSession>> testServers = null;

            try
            {
                List<clientSession> clientSessions = EntityProviderFactory.Instance.getClientSessions(testSessionId);

                if (debugLogEnabled) logger.LogMessage(String.Format("Found {0} client sessions for test session {1}", clientSessions.Count, testSessionId));


                testServers = new Dictionary<Guid, List<ClientSession>>();

                foreach (clientSession clientSession in clientSessions)
                {
                    ClientSession clientSessionPOCO = Z4SModelConverter.ConvertToPOCO(clientSession, zen4SyncSymmetricKeyPassword);

                    if (!testServers.ContainsKey(clientSessionPOCO.clientSession_testServerId))
                    {
                        testServers.Add(clientSessionPOCO.clientSession_testServerId, new List<ClientSession>());
                    }

                    if (debugLogEnabled) logger.LogMessage(String.Format("Adding Client Session {0} on Test Server {1}", clientSessionPOCO.clientSession_id, clientSessionPOCO.clientSession_testServerId));

                    testServers[clientSessionPOCO.clientSession_testServerId].Add(clientSessionPOCO);
                }

                if (debugLogEnabled) logger.LogMessage(String.Format("Calling {0} different Test Servers", testServers.Keys.Count));
                
                foreach (KeyValuePair<Guid, List<ClientSession>> testServer in testServers)
                {
                    testServer testServerEntity = EntityProviderFactory.Instance.GetTestServer(testServer.Key);
                    ChannelFactory<ITestServerServices> testServerClient = new ChannelFactory<ITestServerServices>(testServerEntity.testServer_wcfConfigSectionName);
                    testServerClient.Call(services => services.StartClientSessions(testServer.Value, Z4SModelConverter.ConvertToPOCO(testServerEntity)));
                    testServerClient.Close();
                }
            }
            catch (Exception exc)
            {
                if (debugLogEnabled) logger.LogError(String.Format("Message :\n{0}\nStack Trace :\n{1}\nInner Exeption :\n{2}",exc.Message, exc.StackTrace, (exc.InnerException != null ? exc.InnerException.Message : "null")));
                else throw exc;
            }
            finally
            {
                if (debugLogEnabled && logger.IsOpened) logger.Close();
            }
        }

        /// <summary>
        /// The TestServer informs the Orchestrator that a ClientSession is ready to start.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="processId">Identifier of the process.</param>
        /// <param name="dateTime">Date and time when the ClientSession is ready to start.</param>
        public void ClientSessionIsReadytoStart( Guid clientSessionId, int processId, DateTime dateTime )
        {
            LoggerFactory.Instance.ClientSessionIsReadyToStart( clientSessionId, processId, dateTime );
        }

        /// <summary>
        /// The TestServer informs the Orchestrator that a ClientSession is started.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="dateTime">Date and time when the ClientSession is started.</param>
        public void ClientSessionStarted( Guid clientSessionId, DateTime dateTime )
        {
            LoggerFactory.Instance.ClientSessionStarted( clientSessionId, dateTime );
        }

        /// <summary>
        /// The TestServer informs the Orchestrator that a ClientSession ended its work.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="dateTime">Date and time when the ClientSession ended its work.</param>
        public void ClientSessionEndedSuccessfully( Guid clientSessionId, DateTime dateTime )
        {
            LoggerFactory.Instance.ClientSessionEndedSuccessfully( clientSessionId, dateTime );
        }

        /// <summary>
        /// The TestServer informs the Orchestrator that a ClientSession ended its work with an error.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="dateTime">Date and time when the ClientSession ended its work with an error.</param>
        /// <param name="exceptionMessage">Error message.</param>
        public void ClientSessionEndedWithError( Guid clientSessionId, DateTime dateTime, string exceptionMessage )
        {
            LoggerFactory.Instance.ClientSessionEndedWithError( clientSessionId, dateTime, exceptionMessage );
        }

        /// <summary>
        /// The Console informs the Orchestrator that an activity of a ClientSession is started.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="dateTime">Date and time when the activity starts.</param>
        public void ActivityStarted( Guid clientSessionId, Guid activityId, DateTime dateTime )
        {
            LoggerFactory.Instance.ActivityStarted( clientSessionId, activityId, dateTime );
        }

        /// <summary>
        /// The Console informs the Orchestrator that an activity of a ClientSession is ended.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="dateTime">Date and time when the activity ends.</param>
        public void ActivityEndedSuccessfully( Guid clientSessionId, Guid activityId, DateTime dateTime )
        {
            LoggerFactory.Instance.ActivityEndedSuccessfully( clientSessionId, activityId, dateTime );
        }

        /// <summary>
        /// The Console informs the Orchestrator that an activity of a ClientSession is ended with an error.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="dateTime">Date and time when the activity ends with an error.</param>
        /// <param name="exceptionMessage">Error message.</param>
        public void ActivityEndedWithError( Guid clientSessionId, Guid activityId, DateTime dateTime, string exceptionMessage )
        {
            LoggerFactory.Instance.ActivityEndedWithError( clientSessionId, activityId, dateTime, exceptionMessage );
        }

        /// <summary>
        /// The Console logs the execution context of a mefExecution to the Orchestrator.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="metadata">Metadata of the activity.</param>
        public void LogMefExecution( Guid clientSessionId, Guid activityId, string metadata )
        {
            LoggerFactory.Instance.LogMefExecution( clientSessionId, activityId, metadata );
        }

        /// <summary>
        /// The Console logs the execution context of a sqlceExcution to the Orchestrator.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="sqlceExecutionContext">Execution context of the sqlceExecution.</param>
        public void LogSqlCEExecution( Guid clientSessionId, Guid activityId, Zen4SyncPOCO.POCO.SQLCEExecution.SqlceExecutionContext sqlceExecutionContext )
        {
            LoggerFactory.Instance.LogSqlCEexecution( clientSessionId, activityId, Z4SModelConverter.ConvertToEntity( sqlceExecutionContext ) );
        }

        /// <summary>
        /// The Console logs the execution context of a sqlServerExecution to the Orchestrator.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="sqlServerExecutionContext">Execution context of the sqlServerExecution.</param>
        public void LogSqlServerExecution( Guid clientSessionId, Guid activityId, Zen4SyncPOCO.POCO.SQLServerExecution.SqlServerExecutionContext sqlServerExecutionContext )
        {
            LoggerFactory.Instance.LogSQLServerExecution( clientSessionId, activityId, Z4SModelConverter.ConvertToEntity( sqlServerExecutionContext ) );
        }

        /// <summary>
        /// The Console logs the SQLCE database to the Orchestrator.
        /// </summary>
        /// <param name="clientSessionId">Unique identifier of the ClientSession.</param>
        /// <param name="activityId">Unique identifier of the activity.</param>
        /// <param name="sqlCeDatabase">sqlceDatabase .sdf file.</param>
        public void LogBackupActivity( Guid clientSessionId, Guid activityId, byte[] sqlCeDatabase )
        {
            LoggerFactory.Instance.LogBackupActivity( clientSessionId, activityId, sqlCeDatabase );
        }

        /// <summary>
        /// Reports a Client Session as unable to start because of the given error.
        /// </summary>
        /// <param name="clientSessionId">Identifier of the Client Session.</param>
        /// <param name="error">Problem while trying to start the Client Session.</param>
        public void ClientSessionUnableToStart(Guid clientSessionId, string error)
        {
            LoggerFactory.Instance.ClientSessionUnableToStart( clientSessionId, error );
        }

        /// <summary>
        /// Duplicate a TestSession with its ClientSessions and ActivityContext related to the ClientSessions.
        /// </summary>
        /// <param name="testSessionId">Unique identifier of the TestSession.</param>
        /// <param name="scheduledStartDateTime">Date and time when the new TestSession must run.</param>
        /// <param name="newTestSessionId">Unique identifier of the new TestSession (could be NULL).</param>
        /// <param name="newTestSessionName">Name of the new TestSession (could be NULL).</param>
        /// <param name="newTestSessionCreatedOn">Date and Time when the TestSession was duplicated.</param>
        public Guid DuplicateTestSession( Guid testSessionId, DateTime scheduledStartDateTime, Guid newTestSessionId, string newTestSessionName, DateTime newTestSessionCreatedOn )
        {
            using ( Zen4SyncRepositoryEntities z4sEntities = new Zen4SyncRepositoryEntities() )
            {
                ObjectParameter newTestSession_id = new ObjectParameter( "newTestSession_id", typeof( Guid ) );
                newTestSession_id.Value = newTestSessionId;
                z4sEntities.sp_z4s_duplicate_testSession( testSessionId, scheduledStartDateTime, newTestSession_id, newTestSessionName, newTestSessionCreatedOn );
                return (Guid)newTestSession_id.Value;
            }

        }

        /// <summary>
        /// Duplicate a TestSession with its ClientSessions and ActivityContext related to the ClientSessions.
        /// </summary>
        /// <param name="testSessionId">Unique identifier of the TestSession.</param>
        /// <param name="seconds">Number of seconds to add to the current time to obtain the start DateTime of the TestSession.</param>
        /// <param name="newTestSessionId">Unique identifier of the new TestSession (could be NULL).</param>
        /// <param name="newTestSessionName">Name of the new TestSession (could be NULL).</param>
        /// <param name="newTestSessionCreatedOn">Date and Time when the TestSession was duplicated.</param>
        public Guid DuplicateTestSessionRelative( Guid testSessionId, int seconds, Guid newTestSessionId, string newTestSessionName, DateTime newTestSessionCreatedOn )
        {
            using ( Zen4SyncRepositoryEntities z4sEntities = new Zen4SyncRepositoryEntities() )
            {
                ObjectParameter newTestSession_id = new ObjectParameter( "newTestSession_id", typeof( Guid ) );
                newTestSession_id.Value = newTestSessionId;
                z4sEntities.sp_z4s_duplicate_testSession_relative( testSessionId, seconds, newTestSession_id, newTestSessionName, newTestSessionCreatedOn );
                return (Guid) newTestSession_id.Value;
            }
        }
 
    }
}
